﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SmartMathLibrary;

namespace SmartMathLibrary
{
    /// <summary>
    /// This class provides the scanning or sampling of a bivariate functions.
    /// </summary>
    [Serializable]
    public class BivariateFunctionSampler
    {
        /// <summary>
        /// The sampling interval for the x direction.
        /// </summary>
        private double samplingIntervalX;

        /// <summary>
        /// The sampling interval for the y direction.
        /// </summary>
        private double samplingIntervalY;

        /// <summary>
        /// The function to sample.
        /// </summary>
        private IHardBivariateRealFunction function;

        /// <summary>
        /// Initializes a new instance of the <see cref="BivariateFunctionSampler"/> class.
        /// </summary>
        /// <param name="function">The function to sample.</param>
        public BivariateFunctionSampler(IHardBivariateRealFunction function)
        {
            if (function == (IHardBivariateRealFunction) null)
            {
                throw new ArgumentNullException("function");
            }

            this.function = function;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="BivariateFunctionSampler"/> class.
        /// </summary>
        /// <param name="function">The function to sample.</param>
        public BivariateFunctionSampler(HardBivariateRealFunction function)
        {
            this.function = function;
        }

        /// <summary>
        /// Gets or sets the function to sample.
        /// </summary>
        /// <value>The function to sample.</value>
        public IHardBivariateRealFunction Function
        {
            get { return function; }
            set { function = value; }
        }

        /// <summary>
        /// Gets the sampling interval for the x direction
        /// </summary>
        /// <value>The sampling interval for the x direction.</value>
        public double SamplingIntervalX
        {
            get { return samplingIntervalX; }
        }

        /// <summary>
        /// Gets the sampling interval for the y direction
        /// </summary>
        /// <value>The sampling interval for the y direction.</value>
        public double SamplingIntervalY
        {
            get { return samplingIntervalY; }
        }

        /// <summary>
        /// Samples the specified bivariate function.
        /// </summary>
        /// <param name="startPoint">The start point of the sampling process.</param>
        /// <param name="endPoint">The end point of the sampling process.</param>
        /// <param name="xSamples">The number of samples in x direction.</param>
        /// <param name="ySamples">The number of samples in y direction.</param>
        /// <returns>The results of the sampling process.</returns>
        public double[,] SampleBivariateFunction(Point2D startPoint, Point2D endPoint, int xSamples, int ySamples)
        {
            if (xSamples <= 0)
            {
                throw new ArgumentException("xSamples <= 0", "xSamples");
            }

            if (ySamples <= 0)
            {
                throw new ArgumentException("ySamples <= 0", "ySamples");
            }

            double xStart = Math.Min(startPoint.X, endPoint.X);
            double yStart = Math.Min(startPoint.Y, endPoint.Y);
            double xDirection = (Math.Max(startPoint.X, endPoint.X) - Math.Min(startPoint.X, endPoint.X)) /
                                (xSamples - 1);
            double yDirection = (Math.Max(startPoint.Y, endPoint.Y) - Math.Min(startPoint.Y, endPoint.Y)) /
                                (ySamples - 1);

            if (xDirection == 0)
            {
                throw new ArgumentException("endPoint.X - startPoint.X = 0");
            }

            if (yDirection == 0)
            {
                throw new ArgumentException("endPoint.Y - startPoint.Y = 0");
            }

            double[,] result = new double[xSamples,ySamples];

            this.samplingIntervalX = xDirection;
            this.samplingIntervalY = yDirection;

            for (int x = 0; x < xSamples; x++)
            {
                for (int y = 0; y < ySamples; y++)
                {
                    result[x, y] = this.function.SolveAt(xStart + (x * xDirection), yStart + (y * yDirection));
                }
            }

            return result;
        }
    }
}